#pragma once
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/net/TcpClient.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/Buffer.h>

#include "detail.hpp"
#include "fields.hpp"
#include "abstract.hpp"
#include "message.hpp"
#include <mutex>
#include <unordered_map>

namespace NightCord
{
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf)
            : _buf(buf)
        {
        }
        virtual size_t readableSize() override // 可读空间
        {
            _buf->readableBytes();
        }
        virtual int32_t peekInt32() override // 读取4字节数据，但不消除
        {
            // muduo库是一个网络库，从缓冲区中取出一个4字节的整型，并从网络字节序转成主机字节序
            return _buf->peekInt32();
        }
        virtual void retrieveInt32() override // 消除4字节数据
        {
            _buf->retrieveInt32();
        }
        virtual int32_t readInt32() override // 读取并消除4字节数据
        {
            return _buf->readInt32();
        }
        virtual std::string retireveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer *_buf;
    };

    class BufferFactory
    {
    public:
        template <typename... Args>
        static BaseMessage::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward(args)...);
        }
    };

    class LVProtocol : public BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<LVProtocol>;
        //|--Len--|--mtype--|--idlen--|--id--|--body--|
        virtual bool canProcessed(const BaseBuffer::ptr &buf) override // 判断能否执行
        {
            if (buf->readableSize() < lenFieldsLength)
                return false;
            int32_t total_len = buf->peekInt32();

            if (buf->readableSize() < (total_len + lenFieldsLength))
                return false;

            return true;
        }
        virtual bool OnMessage(const BaseBuffer::ptr &buf, BaseMessage::ptr msg) override // 执行
        {
            int32_t total_len = buf->readInt32();                                         // 读取总长度
            MType mtype = (MType)buf->readInt32();                                        // 读取数据类型
            int32_t idlen = buf->readInt32();                                             // 读取id长度
            std::string id = buf->retireveAsString(idlen);                                // 读取id
            int32_t body_len = total_len - mtypeFieldsLength - idlenFieldsLength - idlen; // 计算body长度
            std::string body = buf->retireveAsString(body_len);                           // 读取body

            msg = MessageFactory::create(mtype);

            if (msg.get() == nullptr)
            {
                ELOG("消息类型错误，构造消息对象失败");
                return false;
            }

            bool ret = msg->unserialize(body);
            if (ret == false)
            {
                ELOG("消息反序列化错误");
                return false;
            }

            msg->setId(id);
            msg->setMType(mtype);

            return true;
        }
        virtual std::string serialize(const BaseMessage::ptr &msg) override
        {
            //|--Len--|--mtype--|--idlen--|--id--|--body--|
            std::string body = msg->serialize();
            std::string id = msg->rid();
            // 注意主机字节序与网络字节序的转化
            auto mtype = htonl((int32_t)msg->mtype());
            int32_t idlen = htonl(id.size());                                                      // 计算id长度
            int32_t h_total_len = mtypeFieldsLength + idlenFieldsLength + id.size() + body.size(); // 计算总长度
            int32_t n_total_len = htonl(h_total_len);

            // 开始组织数据
            std::string result;
            result.reserve(h_total_len);
            result.append((char *)&n_total_len, lenFieldsLength);
            result.append((char *)&mtype, mtypeFieldsLength);
            result.append((char *)&idlen, idlenFieldsLength);
            result.append(id);
            result.append(body);

            return result;
        }

    private:
        const size_t lenFieldsLength = 4;
        const size_t mtypeFieldsLength = 4;
        const size_t idlenFieldsLength = 4;
    };

    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::ptr &protocol)
            : _conn(conn), _protocol(protocol)
        {
        }
        virtual void send(const BaseMessage::ptr &msg) override
        {
            std::string body = _protocol->serialize(msg);
            _conn->send(body);
        }
        virtual void shutdown() override
        {
            _conn->shutdown();
        }
        virtual bool connected() override // 判断是否是一个是、正常连接
        {
            return _conn->connected();
        }

    private:
        muduo::net::TcpConnectionPtr _conn;
        BaseProtocol::ptr _protocol;
    };

    class ConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward(args)...);
        }
    };

    class MuduoServer : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;
        MuduoServer(int port)
            : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port),
                      "MuduoServer", muduo::net::TcpServer::kReusePort),
              _protocol(ProtocolFactory::create())
        {
        }

        virtual void start() override
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _server.start();  // 开始监听
            _baseloop.loop(); // 开始事件监控
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                DLOG("连接建立");
                auto muduo_conn = ConnectionFactory::create(conn, _protocol);
                {
                    std::unique_lock<std::mutex>(_mutex);
                    _conns.insert({conn, muduo_conn});
                }

                if (_cb_connection)
                    _cb_connection(muduo_conn);
            }
            else
            {
                DLOG("连接断开");
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex>(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                        return;
                    muduo_conn = it->second;
                    _conns.erase(conn);
                }

                if (_cb_close)
                    _cb_close(muduo_conn);
            }
        }

        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp time)
        {
            DLOG("连接有数据到来，开始处理");
            auto base_buf = BufferFactory::create(buf);
            while (true)
            {
                if (_protocol->canProcessed(base_buf) == false)
                {
                    if (base_buf->readableSize() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大");
                        return;
                    }
                    break;
                }

                BaseMessage::ptr msg;
                bool ret = _protocol->OnMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区数据错误");
                    return;
                }

                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex>(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                        return;
                    base_conn = it->second;
                }

                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseProtocol::ptr _protocol;
        std::mutex _mutex;
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };
};
